/*
 * Copyright (c) 2025 [ByteDance Ltd. and/or its affiliates.] and/or its affiliates
 * SPDX-License-Identifier: LGPL v2.1+
*/

#include "FFmpegXORIOEncryptor.h"
#include <algorithm>
#include "FFErrorCodeDef.h"

int FFmpegXORIOEncryptor::writePacket(const uint8_t *buf, int bufSize) {
    if (m_bSkipEncrypt)
        return m_pOriginalIOCtx->write_packet(m_pOriginalIOCtx->opaque, buf, bufSize);
    else
        return FFmpegBaseXORIOCryptor::writePacket(buf, bufSize);
}


int FFmpegXORIOEncryptor::writeBdveTag(bool writeParam) {
    auto pb = m_pCryptIOCtx;

    const auto cryptVersion = writeParam ? CRYPT_VERSION_2 : CRYPT_VERSION_3;
    const auto fileSize = pb->pos;
    const int bdveTagSize = getBdveTagSize(cryptVersion);

    m_bSkipEncrypt = true;

    /**
     * Box-Type         Interpretation
     * padd             padding data
     * bdve             bytedance vesdk tag
     *     crpt         encrypt info tag
     *         type     crypt type
     *         version  crypt version
     *         step     (when crypt type is 1 and version is 2)
     *         length   (when crypt type is 1 and version is 2)
     *         key      (when crypt type is 1 and version is 2)
     *     size         bdve box size, must be last box in file
     */

    avio_wb32(pb, bdveTagSize);
    avio_wb32(pb, MKBETAG('b', 'd', 'v', 'e'));

    avio_wb32(pb, getCrptTagSize(cryptVersion));
    avio_wb32(pb, MKBETAG('c', 'r', 'p', 't'));
    avio_wb32(pb, getType());
    avio_wb32(pb, cryptVersion);

    if (CRYPT_VERSION_2 == cryptVersion) {
        // TODO
    } else if (CRYPT_VERSION_3 == cryptVersion) {
        avio_flush(pb);
        uint8_t dst[32] = {0};
        encryptParamSha256(dst);
        avio_write(pb, dst, 32);
    }

    // always last box in file
    avio_wb32(pb, 12);
    avio_wb32(pb, MKBETAG('s', 'i', 'z', 'e'));
    avio_wb32(pb, bdveTagSize);

    avio_flush(pb);

    m_bSkipEncrypt = false;
    return 0;
}

void FFmpegXORIOEncryptor::buildSrc(uint8_t src[16]) {
    memset(src, 0, 16);
    src[0] = m_length & 0x000000ff;
    src[1] = (m_length & 0x0000ff00) >> 8;
    src[2] = (m_length & 0x00ff0000) >> 16;
    src[3] = (m_length & 0xff000000) >> 24;
    src[4] = m_step & 0x000000ff;
    src[5] = (m_step & 0x0000ff00) >> 8;
    src[6] = (m_step & 0x00ff0000) >> 16;
    src[7] = (m_step & 0xff000000) >> 24;
    src[8] = m_key;
}

static void syncContext(const AVIOContext *src, AVIOContext *dst) {
    dst->buffer = src->buffer;
    dst->buffer_size = src->buffer_size;
    dst->buf_end = src->buf_end;
    dst->buf_ptr = src->buf_ptr;
    dst->write_flag = src->write_flag;
    dst->pos = src->pos;
    dst->eof_reached = src->eof_reached;
    dst->max_packet_size = src->max_packet_size;
    dst->checksum = src->checksum;
    dst->checksum_ptr = src->checksum_ptr;
    dst->seekable = src->seekable;
    //dst->maxsize = src->maxsize;
    dst->error = src->error;
    dst->direct = src->direct;
    dst->checksum = src->checksum;
    //dst->last_time = src->last_time;
    dst->bytes_read = src->bytes_read;
    //dst->seek_count = src->seek_count;
    dst->seekable = src->seekable;
    //dst->writeout_count = src->writeout_count;
    //dst->current_type = src->current_type;
    //dst->orig_buffer_size = src->orig_buffer_size;
    //dst->short_seek_threshold = src->short_seek_threshold;
    dst->ignore_boundary_point = src->ignore_boundary_point;
    //dst->written = src->written;
    dst->buf_ptr_max = src->buf_ptr_max;
    dst->min_packet_size = src->min_packet_size;
}

int FFmpegXORIOEncryptor::getBdveTagSize(int version) {
    switch (version) {
        case CRYPT_VERSION_1: return 36;
        case CRYPT_VERSION_2: return 52;
        case CRYPT_VERSION_3: return 36 + 32;
        default: return 0;
    }
}

int FFmpegXORIOEncryptor::getCrptTagSize(int version) {
    switch (version) {
        case CRYPT_VERSION_1: return 16;
        case CRYPT_VERSION_2: return 32;
        case CRYPT_VERSION_3: return 16 + 32;
        default: return 0;
    }
}
